package com.nifostasky.comic.utils;

import com.nifostasky.comic.config.TokenConfig;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 *  jwt加密工具类
 */
@Component
@Slf4j
public class JwtUtil {

    /**
     * token配置
     */
    @Autowired
    private TokenConfig tokenConfig;

    @Autowired
    private RedisTemplate redisTemplate;

    //@Value("${jwt.secret}")
    private String jwtSecret = "fssFSS";

    public String getJwtSecret() {
        return jwtSecret;
    }

    /**
     * 无参构造不提供(避免redisTemplate不存在)
     * @param redisTemplate
     */
    public JwtUtil(RedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
    }

    //无参构造
    public JwtUtil(){};


    /**
     * 生成根据默认时间生成token
     * @param id
     * @param issuer
     * @param subject
     * @param userId 用户ID(存放redis时用于作key,作用:不同平台登录根据用户ID获取token,比较token、解析token判断去终端、ip等信息)
     * @return
     * @throws Exception
     */
    public String createJwtToken(String id, String issuer, String subject,Long userId) throws Exception {
        //token 本身有一个过期时间;用于强制用户登录;
        String jwtToken = createJWT(id, issuer, subject, tokenConfig.getTokenRedisExpireTime());
        //存放redis redis中有一个过期时间,如果用户连续某段时间未使用本系统,则用户需要重新登录;
        redisTemplate.opsForValue().set(RedisKeysConstantUtil.TOKEN_PREFIX + userId,jwtToken,tokenConfig.getTokenRedisExpireTime(), TimeUnit.HOURS);
        return jwtToken;
    }

    /**
     * 创建jwt
     *
     * @param id      加的 盐（自定义的密文）
     * @param issuer  发布者的url地址   （jwt签发人）
     * @param subject 代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志
     * @return
     * @throws Exception
     */
    public String createJWT(String id, String issuer, String subject, Integer hour) throws Exception {
        // 指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 生成JWT的时间
        long nowMillis = System.currentTimeMillis();

        Date now = new Date(nowMillis);
        // 生成签名的时候使用的秘钥secret，切记这个秘钥不能外露哦。它就是你服务端的私钥，在任何场景都不应该流露出去。
        // 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

        // 下面就是在为payload添加各种标准声明和私有声明了
        JwtBuilder builder = Jwts.builder()             // 这里其实就是new一个JwtBuilder，设置jwt的body
                .setId(id)                  // 设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(now)           // iat: jwt的签发时间
                .setIssuer(issuer)          // issuer：jwt签发人
                .setSubject(subject)        // sub(Subject)：代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .signWith(signatureAlgorithm, jwtSecret); // 设置签名使用的签名算法和签名使用的秘钥
        // 设置过期时间
        if (null != hour && hour > 0) {
            Calendar cd = Calendar.getInstance();//获取一个Calendar对象
            cd.setTime(new Date());
            cd.add(Calendar.HOUR,hour);
//            cd.add(Calendar.DATE, day);
            // cd.set(Calendar.d, cd.get(Calendar.MONTH) + day);月
            Date time = cd.getTime();
            builder.setExpiration(time);
        }
        return builder.compact();
    }

    /* *//**
     * 加密
     *
     * @return
     *//*
    public SecretKey generalKey() {

        String stringKey = jwtConstant.JWT_SECRET;
        // 本地的密码解码
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        // 根据给定的字节数组使用AES加密算法构造一个密钥
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }*/

    /**
     * 解密
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public Claims parseJWT(String jwt) {
        //  SecretKey key = generalKey();  //签名秘钥，和生成的签名的秘钥一模一样
        Claims claims = null;     //设置需要解析的jwt
        try {
            claims = Jwts.parser()               //得到DefaultJwtParser
                    .setSigningKey(jwtSecret)                 //设置签名的秘钥
                    .parseClaimsJws(jwt).getBody();
            System.out.println(claims);
        } catch (ExpiredJwtException e) {
            //不管是否过期，都返回claims对象
            claims = e.getClaims();
//            e.printStackTrace();
            log.warn("解析token异常(过期):{}",e);
        }
        return claims;
    }
}
